package com.enlighten.coinDataFetcher;

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpMethod;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.multipart.MultipartRequestEntity;
import org.apache.commons.httpclient.methods.multipart.Part;
import org.apache.commons.httpclient.methods.multipart.StringPart;

public class ForonumCoinDataFetcher implements ICoinDataFetcher{

	private HttpClient httpClient;
	private HttpMethod initialHitMethod;
	private static String initialUrl = "http://www.foronum.com/catalogo-de-monedas.php?id=2";

	private static final Pattern wholeCoinPattern = Pattern.compile("<table[^>]+class=\"caja_ppal\"[^>]*+>(.*?<table[^>]*>.*?</table>.*?)</table>",Pattern.CASE_INSENSITIVE | Pattern.DOTALL);
	private static final Pattern coinImagePattern = Pattern.compile("<a[^>]+href=\"([^\"]++)\"[^>]*+>\\s*<img",Pattern.CASE_INSENSITIVE | Pattern.DOTALL);
	private static final Pattern coinNamePattern = Pattern.compile("<td[^>]+class=\"titulo_moneda\"[^>]*+>\\s*([^-]+)",Pattern.CASE_INSENSITIVE | Pattern.DOTALL);
	private static final Pattern coinMaterialPattern = Pattern.compile("<td[^>]*+>\\s*Material[^<]*+</td>\\s*<td[^>]*+>\\s*([^<]*+)</td>",Pattern.CASE_INSENSITIVE | Pattern.DOTALL);
	private static final Pattern coinWeightPattern = Pattern.compile("<td[^>]*+>\\s*Peso[^<]*+</td>\\s*<td[^>]*+>\\s*[^\\d<]*([\\d,]+)?[^<]*</td>",Pattern.CASE_INSENSITIVE | Pattern.DOTALL);
	private static final Pattern coinDiameterPattern = Pattern.compile("<td[^>]*+>\\s*Diametro[^<]*+</td>\\s*<td[^>]*+>\\s*[^\\d<]*([\\d,]+)?[^<]*</td>",Pattern.CASE_INSENSITIVE | Pattern.DOTALL);
	private static final Pattern coinCantoPattern = Pattern.compile("<td[^>]*+>\\s*Canto[^<]*+</td>\\s*<td[^>]*+>\\s*([^<]+)",Pattern.CASE_INSENSITIVE | Pattern.DOTALL);
	private static final Pattern coinKMPattern = Pattern.compile("<td[^>]*>\\s*#\\s*KM\\s*([^<]+)</td>",Pattern.CASE_INSENSITIVE | Pattern.DOTALL);
	private static final Pattern currentPagePattern = Pattern.compile("<input[^>]+value=\">(\\d+)<\"[^>]*+>",Pattern.CASE_INSENSITIVE | Pattern.DOTALL);
	
	private static final String HOST_URL = "http://www.foronum.com/";
	
	public ForonumCoinDataFetcher() {
		httpClient = new HttpClient();
	}
	
	@Override
	public void initialize() {
		initialHitMethod = new GetMethod(initialUrl);
	}

	@Override
	public List<CoinData> fetchCoinData() {
		List<CoinData> coinsData = new ArrayList<CoinData>();
		HttpMethod methodToUse = initialHitMethod;
		try {
			while ( true ){
				httpClient.executeMethod(methodToUse);
				
				String responseBodyAsString = methodToUse.getResponseBodyAsString();
				
				List<CoinData> parseCoinData = parseCoinData(responseBodyAsString);
				if ( parseCoinData.isEmpty() )
					break;

				coinsData.addAll(parseCoinData);
				methodToUse = getNextPageMethod(methodToUse, responseBodyAsString);
			}
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
		return coinsData;
	}

	private List<CoinData> parseCoinData(String responseBodyAsString) {
		List<CoinData> coinsData = new ArrayList<CoinData>();
		Matcher matcher = wholeCoinPattern.matcher(responseBodyAsString);
		while( matcher.find() ){
			String wholeCoinString = matcher.group(1);
			CoinData coinData = new CoinData();
			
			Matcher coinInfoMatcher = coinImagePattern.matcher(wholeCoinString);
			if ( coinInfoMatcher.find() ) coinData.setImageUrl(HOST_URL+coinInfoMatcher.group(1));
			
			coinInfoMatcher = coinNamePattern.matcher(wholeCoinString);
			if ( coinInfoMatcher.find() ) coinData.setName(coinInfoMatcher.group(1).trim());
			
			coinInfoMatcher = coinMaterialPattern.matcher(wholeCoinString);
			if ( coinInfoMatcher.find() ) coinData.setMaterial(coinInfoMatcher.group(1).trim());
			
			coinInfoMatcher = coinWeightPattern.matcher(wholeCoinString);
			if ( coinInfoMatcher.find() && coinInfoMatcher.groupCount() == 1 ){
				String trim = (coinInfoMatcher.group(1) == null ? "0" : coinInfoMatcher.group(1)).trim();
				double parseDouble = Double.parseDouble(trim.replaceAll(",", "."));
				coinData.setWeight(parseDouble);
			}

			coinInfoMatcher = coinDiameterPattern.matcher(wholeCoinString);
			if ( coinInfoMatcher.find() && coinInfoMatcher.groupCount() == 1 ){
				String trim = (coinInfoMatcher.group(1) == null ? "0" : coinInfoMatcher.group(1)).trim();
				double parseDouble = Double.parseDouble(trim.replaceAll(",", "."));
				coinData.setDiameter(parseDouble);
			}

			coinInfoMatcher = coinCantoPattern.matcher(wholeCoinString);
			if ( coinInfoMatcher.find() ) coinData.setCanto(coinInfoMatcher.group(1).trim());

			coinInfoMatcher = coinKMPattern.matcher(wholeCoinString);
			if ( coinInfoMatcher.find() ) coinData.setKm(coinInfoMatcher.group(1).trim());

			coinsData.add(coinData);
		}
		return coinsData;
	}

	private HttpMethod getNextPageMethod(HttpMethod methodToUse, String lastResponseBody) {
		PostMethod postMethod = new PostMethod(initialUrl);
		int nextPage = getCurrentPageNumber(lastResponseBody) + 1;
		Part[] parts = {
				new StringPart(nextPage+"", nextPage+""),
				new StringPart("pagina", nextPage+""),
				new StringPart("mas", "1"),
				new StringPart("b_max", "10")
				};

		postMethod.setRequestEntity(
			      new MultipartRequestEntity(parts, postMethod.getParams())
			      );

		return postMethod;
	}

	private int getCurrentPageNumber(String lastResponseBody) {
		Matcher matcher = currentPagePattern.matcher(lastResponseBody);
		if ( matcher.find() )
			return Integer.parseInt(matcher.group(1));
		return -2;
	}
}
